/**
  */

package org.rescue.Dag;

import java.util.*;

/**
 * Node of a taxonomy which is described by a Directed Acyclic Graph (DAG). <p>
 * This means that any node may contain multiple children and
 * multiple parents. Cycles are not allowed. <p>
 *
 * The node may contain any object (Concept for example), just its
 * key or both. <p>
 */
public class DAGNode
{
    // -----------------------------------------------------------------------
    //  Variables declarations
    // -----------------------------------------------------------------------
    /** Is node a root node? */
    public boolean root;

    /** Is node a leaf node? */
    public boolean leaf;

    /** Level of the node (if defined).  */
    public int level;

    /** Children of the node. */
    public DAGNode[] children;

    /** Parents of this node. */
    public DAGNode[] parents;

    /** Node value object. */
    private Object nodeValue;

    /** Key to node value object. */
    private int nodeValueKey = -1;

   //  public int nRPost = 0;  // postorder traversal flag
    
    // -----------------------------------------------------------------------
    //  Methods of node manipulation
    // -----------------------------------------------------------------------
    /**
     * Get number of childs.
     *
     * @return number of childs
     */
    public int getChildCount()
    {
        return ( children != null ) ? children.length : 0;
    }

    /**
     * Get child node at specified index.
     *
     * @param childIndex index of child
     * @return child at specified index, null if not found
     */
    public DAGNode getChildAt( int childIndex )
    {
        return ( children != null ) ? children[childIndex] : null;
    }

    /**
     * Get index of child node.
     *
     * @param childNode child node
     * @return index child index, -1 if not found
     */
    public int getChildIndex( DAGNode childNode )
    {
        int index = -1;
        if( children == null )
        {
            index = -1;
        }
        else
        {
            for( int i = 0; i < children.length; i++ )
            {
                if( children[i].equals( childNode ) )
                {
                    index = i;
                }
            }
        }
        return index;
    }

    /**
     * Return list of all children
     *
     * @return all children
     */
    public Enumeration<DAGNode> children()
    {
        if( children != null )
        {
            Vector<DAGNode> v = new Vector<DAGNode> ( Arrays.asList( children ) );
            return v.elements();
        }
        else
        {
            return null;
        }
    }

    /**
    * Add child node to children list.
    *
    * @param child child node to add
    */
    public void addChildNode(DAGNode child)
    {
        int nchild = getChildCount();
        DAGNode[] child2 = new DAGNode[nchild];

        for (int i = 0; i < nchild; i++)
          child2[i] = (DAGNode) children[i];

        children = new DAGNode[nchild+1];

        for (int i = 0; i < nchild; i++)
          children[i] = child2[i];

        children[nchild] = child;
    }

    /**
     * Remove child from node.
     *
     * @param child child to be removed
     */
    public void removeChild(DAGNode child)
    {
      if(children == null) return;
      int index = getChildIndex(child);
      if(index != -1)
      {
        removeChild(index);
      }
    }

    /**
     * Remove child of given index.
     *
     * @param childIndex index of child to be removed
     */
    public void removeChild(int childIndex)
    {
        DAGNode[] childs = new DAGNode[children.length-1];
        System.arraycopy(children,0,childs,0,childIndex);
        System.arraycopy(children,childIndex+1,childs,childIndex,children.length-childIndex-1);
        children = childs;
        if(children.length == 0) leaf = true;
    }

    /**
     * Remove all children.
     */
    public void removeAllChildren()
    {
      children = null;
      leaf = true;
    }

    /**
     * Get number of parents.
     *
     * @return number of parents
     */
    public int getParentsCount()
    {
        return ( parents != null ) ? parents.length : 0;
    }

    /**
     * Get parent node at specified index.
     *
     * @param parentIndex index of parent
     * @return parent at specified index, null if not found
     */
    public DAGNode getParentAt( int parentIndex )
    {
        return ( parents != null ) ? parents[parentIndex] : null;
    }

    /**
     * Get index of parent node.
     *
     * @param parentNode parent node
     * @return parent index, -1 if not found
     */
    public int getParentIndex( DAGNode parentNode )
    {
        int index = -1;
        if( parents == null )
        {
            index = -1;
        }
        else
        {
            for( int i = 0; i < parents.length; i++ )
            {
                if( parents[i].equals( parentNode ) )
                {
                    index = i;
                }
            }
        }
        return index;
    }

    /**
     * Return list of all parents
     *
     * @return all parents
     */
    public Enumeration<DAGNode> parents()
    {
        if( parents != null )
        {
            Vector<DAGNode> v = new Vector<DAGNode>( Arrays.asList( parents ) );
            return v.elements();
        }
        else
        {
            return null;
        }
    }

    /**
    * Add parent node to parent list.
    *
    * @param parent parent node to add
    */
    public void addParentNode(DAGNode parent)
    {
        int npar = getParentsCount();
        DAGNode[] par2 = new DAGNode[npar];
        for (int i = 0; i < npar; i++)
          par2[i] = (DAGNode) parents[i];
        parents = new DAGNode[npar+1];
        for (int i = 0; i < npar; i++)
          parents[i] = par2[i];
        parents[npar] = parent;
    }

    /**
     * Remove parent from node.
     *
     * @param parent parent to be removed
     */
    public void removeParent(DAGNode parent)
    {
      if(parents == null) return;
      int index = getParentIndex(parent);
      if(index != -1)
      {
        removeParent(index);
      }
    }

    /**
     * Remove parent of given index.
     *
     * @param parentIndex index of parent to be removed
     */
    public void removeParent(int parentIndex)
    {
        DAGNode[] pars = new DAGNode[parents.length-1];
        System.arraycopy(parents,0,pars,0,parentIndex);
        System.arraycopy(parents,parentIndex+1,pars,parentIndex,parents.length-parentIndex-1);
        parents = pars;
        if(parents.length == 0) root = true;
    }

    /**
     * Remove all parents.
     */
    public void removeAllParents()
    {
      parents = null;
      root = true;
    }

    /**
     * Is node a leaf?
     *
     * @return true, if leaf, elso false
     */
    public boolean isLeaf()
    {
        return leaf;
    }

    /**
     * Set node leaf status.
     *
     * @param leaf true if node is leaf, else false
     */
    public void setLeaf( boolean leaf )
    {
        this.leaf = leaf;
    }

    /**
     * Is node a root?
     *
     * @return true, if root, elso false
     */
    public boolean isRoot()
    {
        return root;
    }

    /**
     * Set node root status.
     *
     * @param root true if node is root, else false
     */
    public void setRoot( boolean root )
    {
        this.root = root;
    }

    /**
     * Return hierarchy level (root level = 0).
     *
     * @return level of node
     */
    public int getLevel()
    {
        return level;
    }

    /**
     * Set level (root level = 0).
     *
     * @param lev level of node
     */
    public void setLevel( int lev )
    {
        this.level = lev;
    }

    /**
     * Get value of node.
     *
     * @return node value
     */
    public Object getNodeValue()
    {
        return nodeValue;
    }

    public Concept getNodeConcept()
    {
        return (Concept)nodeValue;
    }
    
    
    /**
     * Sets value of node.
     *
     * @param nodeValue new value of node
     */
    public void setNodeValue(Object nodeValue)
    {
        this.nodeValue = nodeValue;
    }

    public void setNodeConcept(Concept nodeValue)
    {
        this.nodeValue = nodeValue;
    }
    
    
    /**
     * Returns key of node value.
     *
     * @return key of node value
     */
    public int getNodeValueKey()
    {
        return nodeValueKey;
    }

    /**
     * Sets key of node value.
     *
     * @param nodeValueKey new key of node value
     */
    public void setNodeValueKey(int nodeValueKey)
    {
        this.nodeValueKey = nodeValueKey;
    }
}
